import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import router from '@/router/index'
import { ElMessage, ElNotification } from 'element-plus'
import { useStorage, StorageType } from '../storage'
import env from '@/utils/env'
import md5 from 'crypto-js/md5'
import errorCode from '@/constants/errorCode'
import { getToken } from '@/utils/auth'

interface IAxiosRequestConfig extends AxiosRequestConfig {
  toast?: boolean
  loading?: boolean
}

const Message = (message: any) => {
  ElMessage.error(message)
}

const sessionStorage = useStorage('app', StorageType.SessionStorage)

function join(...args: string[]): string {
  // 校验输入参数是否为字符串
  if (args.some((arg) => typeof arg !== 'string')) {
    throw new Error('All arguments must be strings')
  }

  // 过滤掉空字符串和当前目录（'.'）
  const parts = args.filter((part) => part !== '' && part !== '.')

  // 处理父级路径（'..'）和其他路径部分
  const result: string[] = []
  for (const part of parts) {
    if (part === '..') {
      if (result.length > 0 && result[result.length - 1] !== '..') {
        result.pop()
      } else {
        result.push(part)
      }
    } else {
      // 去掉每个路径部分前面的 '/'，确保路径拼接时只加一个 '/'
      result.push(part.replace(/^\/+/, ''))
    }
  }

  // 如果结果为空，返回根路径 '/'
  if (result.length === 0) {
    return '/'
  }

  // 确保路径从根目录开始（即返回以 '/' 开头的路径）
  return '/' + result.join('/')
}

const ongoingRequests: Record<string, any> = {}
let loadingTimeout: any

class Request {
  // axios 实例
  instance: AxiosInstance
  // 基础配置，url和超时时间
  baseConfig: AxiosRequestConfig = {
    baseURL: '/',
    timeout: 60000,
  }

  constructor(config: IAxiosRequestConfig) {
    // 使用axios.create创建axios实例

    this.instance = axios.create(Object.assign(this.baseConfig, config))

    this.instance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 一般会请求拦截里面加token

        const token = getToken()

        if (config.headers && token) {
          config.headers['Authorization'] = 'Bearer ' + token
        }
        return config
      },
      (err: any) => {
        return Promise.reject(err)
      },
    )

    let logoutTimer: any = null
    this.instance.interceptors.response.use(
      (res: AxiosResponse) => {
        if (res.config.responseType === 'blob') {
          return res.data
        }
        const { code, msg } = res.data
        const message = errorCode[code] || msg || errorCode['default']

        if (code === 200) {
          return {
            ...res.data,
            success: true,
            code: code,
            message,
          }
        }

        if ((res.config as IAxiosRequestConfig).toast !== false) {
          if (code === 500) {
            Message({ message, type: 'error' })
          } else if (code === 601) {
            Message({ message, type: 'warning' })
          }

          return Promise.reject({
            ...res.data,
            success: false,
            code: code,
            message,
          })
        }

        ElNotification.error({ title: message })
        return Promise.reject({
          ...res.data,
          success: false,
          code: code,
          message,
        })
      },
      (err: any) => {
        // 这里用来处理http常见错误，进行全局提示
        let message = ''
        switch (err.response.status) {
          case 400:
            message = '请求错误(400)'
            break
          case 401:
            // 清除已有定时器
            if (logoutTimer) {
              clearTimeout(logoutTimer)
            }
            logoutTimer = setTimeout(() => {
              message = '您的账号发生变更，如：密码变更/权限变更/被他人登录等，请重新登录'
              sessionStorage.remove('S_TOKEN_VALUE')
              Message({
                showClose: true,
                message: `${message}`,
                type: 'error',
              })
              router.replace('/login')
            }, 200)
            return Promise.reject(err.response)
          case 403:
            message = '拒绝访问(403)'
            break
          case 404:
            message = '请求出错(404)'
            break
          case 408:
            message = '请求超时(408)'
            break
          case 500:
            message = '服务器错误(500)'
            break
          case 501:
            message = '服务未实现(501)'
            break
          case 502:
            message = '网络错误(502)'
            break
          case 503:
            message = '服务不可用(503)'
            break
          case 504:
            message = '网络超时(504)'
            break
          case 505:
            message = 'HTTP版本不受支持(505)'
            break
          default:
            message = `连接出错(${err.response.status})!`
        }
        // 这里错误消息可以使用全局弹框展示出来
        // 比如element plus 可以使用 ElMessage
        Message({
          showClose: true,
          message: `${message}，请检查网络或联系管理员！`,
          type: 'error',
        })
        // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
        return Promise.reject(err.response)
      },
    )
  }

  // 定义请求方法
  public request<T = any>(config: IAxiosRequestConfig) {
    const hash = md5(JSON.stringify(config)).toString()

    // if (ongoingRequests[hash]) {
    //   console.warn('重复请求', config.url)
    //   return Promise.resolve<AxiosResult<any>>({
    //     success: false,
    //     code: 'ABORT',
    //     message: '请求太过频繁',
    //     data: null,
    //     abort: true,
    //   })
    // }

    ongoingRequests[hash] = 1

    if (config.loading !== false) {
      clearTimeout(loadingTimeout)
      loadingTimeout = setTimeout(() => {
        window.LD.show()
      }, 100)
    }

    return this.instance.request<T, AxiosResult<T>, IAxiosRequestConfig>(config).finally(() => {
      window.LD.hide()
      clearTimeout(loadingTimeout)
      setTimeout(() => delete ongoingRequests[hash], 300)
    })
  }

  public get<T = any>(url: string, config?: IAxiosRequestConfig): Promise<AxiosResult<T>> {
    return this.request({
      ...config,
      url,
      method: 'GET',
    })
  }

  public post<T = any>(
    url: string,
    data?: any,
    config?: IAxiosRequestConfig,
  ): Promise<AxiosResult<T>> {
    return this.request({
      ...config,
      url,
      data,
      method: 'POST',
    })
  }

  public put<T = any>(
    url: string,
    data?: any,
    config?: IAxiosRequestConfig,
  ): Promise<AxiosResult<T>> {
    return this.request({ ...config, url, data, method: 'PUT' })
  }

  public delete<T = any>(url: string, config?: IAxiosRequestConfig): Promise<AxiosResult<T>> {
    return this.request({
      ...config,
      url,
      method: 'DELETE',
    })
  }

  public blob(url: string, data?: any, config?: IAxiosRequestConfig) {
    return this.request({
      ...config,
      url,
      data,
      responseType: 'blob',
      method: 'POST',
    }) as unknown as Promise<Blob> // 做强制转换待优化
  }
}

const useRequest = (config: IAxiosRequestConfig) => {
  const baseURL = join(env.VITE_API_HOST, config.baseURL || '/')

  console.log(baseURL)

  const request = new Request({ ...config, baseURL })

  return {
    post: <T = any>(url: string, data?: any, config?: IAxiosRequestConfig) =>
      request.post<T>(url, data, config),
    get: <T = any>(url: string, config?: IAxiosRequestConfig) => request.get<T>(url, config),
    put: <T = any>(url: string, data?: any, config?: IAxiosRequestConfig) =>
      request.put<T>(url, data, config),
    delete: <T = any>(url: string, config?: IAxiosRequestConfig) => request.delete<T>(url, config),
    blob: (url: string, data?: any, config?: IAxiosRequestConfig) =>
      request.blob(url, data, config),
  }
}

export default useRequest
